package cn.sourcespro.shiro.controller;

import cn.sourcespro.shiro.crudparams.dto.UserLoginDto;
import cn.sourcespro.shiro.crudparams.vo.Token;
import cn.sourcespro.shiro.crudparams.vo.Vo;
import cn.sourcespro.shiro.entity.User;
import cn.sourcespro.shiro.security.LoginType;
import cn.sourcespro.shiro.security.UpToken;
import cn.sourcespro.shiro.service.PermService;
import cn.sourcespro.shiro.service.RoleService;
import cn.sourcespro.shiro.service.impl.RoleServiceImpl;
import cn.sourcespro.shiro.util.JwtTokenUtil;
import cn.sourcespro.shiro.util.ShiroUtil;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.Set;

/**
 * security
 *
 * @author zhanghaowei
 * @date 2018/7/14
 */
@RestController
public class LoginController {

    private static final Logger logger = LoggerFactory.getLogger(LoginController.class);
    @Autowired
    private RoleService roleService;
    @Autowired
    private PermService permService;

    @PostMapping("rest/login")
    Token appLogin(@RequestBody UserLoginDto loginDto){
        String username = loginDto.getUsername();
        UpToken token = new UpToken();
        token.setUsername(username);
        token.setPassword(loginDto.getPassword().toCharArray());
        token.setLoginType(LoginType.REST_USER);
        Subject currentUser = SecurityUtils.getSubject();
        try {
            logger.info("对用户[" + username + "]进行登录验证..验证开始");
            currentUser.login(token);
            logger.info("对用户[" + username + "]进行登录验证..验证通过");
        } catch(UnknownAccountException uae){
            logger.info("对用户[" + username + "]进行登录验证..验证未通过,未知账户");
            return new Token("未知账户", HttpStatus.FORBIDDEN);
        }catch(IncorrectCredentialsException ice){
            logger.info("对用户[" + username + "]进行登录验证..验证未通过,错误的凭证");
            return new Token("密码不正确", HttpStatus.FORBIDDEN);
        }catch(LockedAccountException lae){
            logger.info("对用户[" + username + "]进行登录验证..验证未通过,账户已锁定");
            return new Token("账户已锁定", HttpStatus.FORBIDDEN);
        }catch(ExcessiveAttemptsException eae){
            logger.info("对用户[" + username + "]进行登录验证..验证未通过,错误次数过多");
            return new Token("用户名或密码错误次数过多", HttpStatus.FORBIDDEN);
        }catch(AuthenticationException ae){
            //通过处理Shiro的运行时AuthenticationException就可以控制用户登录失败或密码错误时的情景
            logger.info("对用户[" + username + "]进行登录验证..验证未通过,堆栈轨迹如下");
            logger.error("{}", ae);
            return new Token("用户名或密码不正确", HttpStatus.FORBIDDEN);
        }
        //验证是否登录成功
        if(currentUser.isAuthenticated()){
            logger.info("用户[" + username + "]登录认证通过");
            User user = (User) SecurityUtils.getSubject().getPrincipal();
            Set<String> roles = roleService.findByUserId(user.getId());
            Set<String> perms = permService.findByUserId(user.getId());
            return new Token("登陆成功", JwtTokenUtil.createAppToken(user.getId(), roles, perms));
        }else{
            token.clear();
            return new Token("认证失败", HttpStatus.FORBIDDEN);
        }
    }

    @PostMapping("login")
    Vo login(@RequestBody UserLoginDto loginDto){
        String username = loginDto.getUsername();
        UsernamePasswordToken token = new UsernamePasswordToken();
        token.setUsername(username);
        token.setPassword(loginDto.getPassword().toCharArray());
        Subject currentUser = SecurityUtils.getSubject();
        try {
            logger.info("对用户[" + username + "]进行登录验证..验证开始");
            currentUser.login(token);
            logger.info("对用户[" + username + "]进行登录验证..验证通过");
        } catch(UnknownAccountException uae){
            logger.info("对用户[" + username + "]进行登录验证..验证未通过,未知账户");
            return new Vo("未知账户", HttpStatus.FORBIDDEN);
        }catch(IncorrectCredentialsException ice){
            logger.info("对用户[" + username + "]进行登录验证..验证未通过,错误的凭证");
            return new Vo("密码不正确", HttpStatus.FORBIDDEN);
        }catch(LockedAccountException lae){
            logger.info("对用户[" + username + "]进行登录验证..验证未通过,账户已锁定");
            return new Vo("账户已锁定", HttpStatus.FORBIDDEN);
        }catch(ExcessiveAttemptsException eae){
            logger.info("对用户[" + username + "]进行登录验证..验证未通过,错误次数过多");
            return new Vo("用户名或密码错误次数过多", HttpStatus.FORBIDDEN);
        }catch(AuthenticationException ae){
            //通过处理Shiro的运行时AuthenticationException就可以控制用户登录失败或密码错误时的情景
            logger.info("对用户[" + username + "]进行登录验证..验证未通过,堆栈轨迹如下");
            logger.error("{}", ae);
            return new Vo("用户名或密码不正确", HttpStatus.FORBIDDEN);
        }
        //验证是否登录成功
        if(currentUser.isAuthenticated()){
            logger.info("用户[" + username + "]登录认证通过(这里可以进行一些认证通过后的一些系统参数初始化操作)");
            return new Vo("登陆成功");
        }else{
            token.clear();
            return new Vo("认证失败", HttpStatus.FORBIDDEN);
        }
    }

    @GetMapping("logout")
    Vo logout(){
        try {
            Subject subject = SecurityUtils.getSubject();
            User user = (User) subject.getPrincipal();
            logger.info("[{}]退出登录");
            ShiroUtil.kickOutUser(user.getUsername(), false);
            //使用权限管理工具进行用户的退出，跳出登录，给出提示信息
            subject.logout();
            logger.info("[{}]退出登录成功");
        } catch (Exception e) {
            logger.error("退出失败，{}", e);
            return new Vo("退出失败", HttpStatus.FORBIDDEN);
        }
        return new Vo("退出成功！");
    }

}
